use serde::{Deserialize, Serialize};
use abi::error::Error;

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Config {
    pub srv: ServerConfig,
    pub db: DbConfig,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct ServerConfig {
    pub host: String,
    pub port: u16,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct DbConfig {
    pub host: String,
    pub port: u16,
    pub user: String,
    pub password: String,
    pub dbname: String,
    #[serde(default = "default_pool_size")]
    pub pool_size: u32,
}

fn default_pool_size() -> u32 {
    10
}

impl Config {
    pub fn load(filename: &str) -> Result<Self, Error> {
        let content = std::fs::read_to_string(filename).map_err(|_| Error::ConfigError)?;
        Ok(toml::from_str(&content).map_err(|_| Error::ConfigError)?)
    }
}

impl DbConfig {
    pub fn get_db_url(&self) -> String {
        if self.password.is_empty() {
            format!(
                "postgres://{}@{}:{}/{}",
                self.user, self.host, self.port, self.dbname
            )
        } else {
            format!(
                "postgres://{}:{}@{}:{}/{}",
                self.user, self.password, self.host, self.port, self.dbname
            )
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn config_should_be_loaded() {
        let config = Config::load("../config.toml").expect("Failed to load config");
        assert_eq!(
            config,
            Config {
                srv: ServerConfig {
                    host: "127.0.0.1".to_string(),
                    port: 8080
                },
                db: DbConfig {
                    host: "localhost".to_string(),
                    port: 5432,
                    user: "postgres".to_string(),
                    password: "passwd".to_string(),
                    dbname: "reservations".to_string(),
                    pool_size: 10
                }
            }
        )
    }
}
